﻿using Microsoft.Win32;
using OdDrawingsExamplesMgdLib.ExEditors;
using SmartPlant.OdDrawingsLib.ExView;
using SmartPlant.OdKernelLib.ExView;
using SmartPlant.OdKernelUILib.ExView;
using SmartPlant.WinCore.Helper;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Teigha.Core;
using Teigha.TD;

namespace SmartPlant.CADView
{
    /// <summary>
    /// This UserControl render only database of OdDbDatabase type.
    /// Rendering model done by means of DirectX only, as image source of type D3DImage
    /// </summary>
    public partial class OdExViewDirectXWpfControl : System.Windows.Controls.UserControl
    {
        /// <summary>
        /// Current database
        /// </summary>
        private OdDbDatabase m_database;

        /// <summary>
        /// Example render device for database of OdDbDatabase type, which have base functionality for rendering.
        /// </summary>
        private OdExViewDrawingRenderDevice m_RenderDevice;

        /// <summary>
        /// Example D3DImage on which do rendering D3DSurface
        /// </summary>
        private OdExRenderD3DImage m_D3DImage;

        /// <summary>
        /// Example Pan functionality
        /// </summary>
        private OdExViewPanWpf m_panFunctionality;

        /// <summary>
        /// Example Zoom functionality
        /// </summary>
        private OdExViewZoomWpf m_zoomFunctionality;

        /// <summary>
        /// Example Rotation functionality
        /// </summary>
        private OdExViewRotationWpf m_rotationFunctionality;

        /// <summary>
        /// Example Editor functionality
        /// </summary>
        private OdExDrawingEditorWpf m_mouseEditorFunctionality;

        public OdGsLayoutHelper m_GsLayoutHelper { get { return OdGsLayoutHelper.cast(m_RenderDevice.m_GsDevice); } }

        public OdExViewDirectXWpfControl()
        {
            InitializeComponent();
            m_RenderDevice = new OdExViewDrawingRenderDevice(OdExGsDeviceCreator.OdExGsModuleType.WinDirectX);
            m_D3DImage = new OdExRenderD3DImage();

            this.SizeChanged += SizeChangedHandler;

            renderImage.Source = GetOdaLogo();
            m_panFunctionality = new OdExViewPanWpf(this);
            m_zoomFunctionality = new OdExViewZoomWpf(this);
            m_rotationFunctionality = new OdExViewRotationWpf(this);
            m_mouseEditorFunctionality = new OdExDrawingEditorWpf(this);

            SystemEvents.DisplaySettingsChanging += SystemEvents_DisplaySettingsChanged;
        }

        /// <summary>
        /// If display properties was been changed, that need reinitialize rendering
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
        {
            if (!IsInitialized)
                return;

            EventHandler reInitializeHandler = new EventHandler((object s, EventArgs ea) =>
            {
                (s as DispatcherTimer).Stop();
                ReInit();
            });

            DispatcherTimer timer = new DispatcherTimer(
              new TimeSpan(10000),
              DispatcherPriority.ApplicationIdle,
              reInitializeHandler,
              this.Dispatcher
              );

            timer.Start();
        }

        /// <summary>
        /// Method enable/disable zoom by mouse wheel 
        /// </summary>
        /// <param name="state"></param>
        public void EnableZoom(bool state)
        {
            EnableFunctionality(m_zoomFunctionality, state);
        }

        /// <summary>
        /// Method enable/disable pan by mouse
        /// </summary>
        /// <param name="state"></param>
        public void EnablePan(bool state)
        {
            EnableFunctionality(m_panFunctionality, state);
        }

        /// <summary>
        /// Method enable/disable rotation by mouse
        /// </summary>
        /// <param name="state"></param>
        public void EnableRotation(bool state)
        {
            EnableFunctionality(m_rotationFunctionality, state);
        }

        /// <summary>
        /// Method enable/disable editor by mouse (selection, grip points, and etc)
        /// </summary>
        /// <param name="state"></param>
        public void EnableMouseEditor(bool state)
        {
            EnableFunctionality(m_mouseEditorFunctionality, state);
        }


        /// <summary>
        /// Is rendering already initialized, so that method return true; otherwise false;
        /// </summary>
        /// <returns></returns>
        public bool IsRenderingInitialized()
        {
            if (m_RenderDevice == null)
                return false;
            return m_RenderDevice.IsInitialized;
        }

        /// <summary>
        /// Method release surface object which use in D3DImage.
        /// Before some any action with OdGsDevice, need release surface on Image
        /// </summary>
        public void OnReleaseImageSurface()
        {
            System.Windows.Application.Current.Dispatcher.Invoke((Action)m_D3DImage.ReleaseSurface);
        }

        /// <summary>
        /// Method update surface object which use in D3DImage.
        /// After some any action with OdGsView, need update surface on Image
        /// This method need execute after OnReleaseImageSurface() method
        /// </summary>
        public void OnUpdateImageSurface()
        {
            System.Windows.Application.Current.Dispatcher.Invoke((Action)m_D3DImage.UpdateSurface);
        }

        /// <summary>
        /// Method do zoom to extents
        /// </summary>
        public void ZoomToExtents()
        {
            if (!m_RenderDevice.IsInitialized)
                return;

            m_RenderDevice.OnZoomToExtents();
        }

        /// <summary>
        /// Method initialize rendering. After initialization will be rendering database of OdDbDatabase type
        /// </summary>
        /// <param name="database"></param>
        public void Init(OdDbDatabase database)
        {
            if (m_RenderDevice.IsInitialized)
                throw new Exception("Render device already initialized. Please uninitialize before.");
            m_database = database;
            InitRenderDevice(m_database);
            InitAddittionalFunctionality(m_database);
        }

        /// <summary>
        /// Method uninitialize rendering
        /// </summary>
        public void UnInit()
        {
            if (!m_RenderDevice.IsInitialized)
                return;
            UnInitializeAdditionalFunctional();
            UnInitRenderDevice();
        }

        /// <summary>
        /// Method reinitialize rendering
        /// </summary>
        private void ReInit()
        {
            //Uninitialize rendering
            UnInit();

            //Create new instance D3DImage, because DirectX device may changed
            m_D3DImage = new OdExRenderD3DImage();

            //Initialize rendering
            Init(m_database);
        }

        #region AddtitionalFuntionality 
        private void EnableFunctionality(OdExViewFunctionalityInterface functionality, bool state)
        {
            if (functionality.IsEnable == state)
                return;

            functionality.IsEnable = state;
            if (state)
            {
                functionality.InitEvents();
                functionality.PostPaintEvent += PostPaintEventHandler;
                functionality.PrePaintEvent += PrePaintEventHandler;
            }
            else
            {
                functionality.UnInitEvents();
                functionality.PostPaintEvent -= PostPaintEventHandler;
                functionality.PrePaintEvent -= PrePaintEventHandler;
            }
        }

        private void InitAddittionalFunctionality(OdDbDatabase database)
        {
            if (!m_RenderDevice.IsInitialized)
                throw new Exception("m_RenderDevice not initialized yet");
            m_panFunctionality.TargetView = m_GsLayoutHelper.activeView();
            m_zoomFunctionality.TargetView = m_GsLayoutHelper.activeView();
            m_rotationFunctionality.TargetView = m_GsLayoutHelper.activeView();
            m_mouseEditorFunctionality.Init(database, m_GsLayoutHelper);
        }

        private void UnInitializeAdditionalFunctional()
        {
            m_panFunctionality.TargetView = null;
            m_zoomFunctionality.TargetView = null;
            m_rotationFunctionality.TargetView = null;
            m_mouseEditorFunctionality.UnInit();
        }
        #endregion

        private void InitRenderDevice(OdDbDatabase database)
        {
            try
            {

                //Must Initialize handlers for events
                m_RenderDevice.NeedClientRectangleSizeEvent += GetClientRectangleSizeEventHandler;
                m_RenderDevice.PrePaintEvent += PrePaintEventHandler;
                m_RenderDevice.PostPaintEvent += PostPaintEventHandler;

                //LoggerHelper.Current.Info("GetHWND_FromHiddenWindowForRendering");
                //Need create hidden window, so that OdGsDevice render model on window
                HwndSource renderWindow = GetHWND_FromHiddenWindowForRendering();

                //LoggerHelper.Current.Info("InitRenderDevice");
                //Initialize Render Device
                m_RenderDevice.InitRenderDevice(database, renderWindow.Handle, IntPtr.Zero);

                //LoggerHelper.Current.Info("Initialize");
                //Set Target OdGsDevice
                m_D3DImage.Initialize(m_RenderDevice.m_GsDevice);

                //LoggerHelper.Current.Info("UpdateSizeGsDevice");
                //Initial update size;
                m_RenderDevice.UpdateSizeGsDevice();

                //Set ImageSource
                renderImage.Source = m_D3DImage;
            }
            catch (Exception err)
            {
                LoggerHelper.Current.Error(err.Message, err);
                LoggerHelper.Current.Error(err.StackTrace, err);
                System.Windows.MessageBox.Show(err.Message, "初始化错误");
                renderImage.Source = GetOdaLogo();
            }
        }

        private void UnInitRenderDevice()
        {
            try
            {
                //Must uninitialize handlers for events
                m_RenderDevice.NeedClientRectangleSizeEvent -= GetClientRectangleSizeEventHandler;
                m_RenderDevice.PrePaintEvent -= PrePaintEventHandler;
                m_RenderDevice.PostPaintEvent -= PostPaintEventHandler;

                //Uninitialize Render Device
                m_RenderDevice.UnInitRenderDevice();

                //Uninitialize D3DSurface Image data
                m_D3DImage.UnInitializeSurface();

            }
            catch (Exception err)
            {
                LoggerHelper.Current.Error(err.Message, err);
                LoggerHelper.Current.Error(err.StackTrace, err);
                System.Windows.MessageBox.Show(err.Message, "释放资源错误");
            }
            finally
            {
                renderImage.Source = GetOdaLogo();
            }
        }

        private void PrePaintEventHandler()
        {
            OnReleaseImageSurface();
        }

        private void PostPaintEventHandler()
        {
            OnPostPaintUpdate();
        }

        public void OnPostPaintUpdate()
        {
            System.Windows.Application.Current.Dispatcher.Invoke((Action)m_RenderDevice.RegenCoef);
            System.Windows.Application.Current.Dispatcher.Invoke((Action)m_RenderDevice.UpdateGsDevice);

            OnUpdateImageSurface();
        }

        public void OnPrePaintUpdate()
        {
            OnReleaseImageSurface();
        }

        private OdGsDCRect GetClientRectangleSizeEventHandler()
        {
            var rect = this.RenderSize;
            int Left = (int)0;
            int Right = (int)rect.Width;
            int Top = (int)0;
            int Bottom = (int)rect.Height;
            return new OdGsDCRect(Left, Right, Bottom, Top);
        }

        /// <summary>
        /// This hidden window is need, so that OdGsDevice can do rendering 
        /// But, we use result image as D3DSurface, from OdGsDevice
        /// </summary>
        private Window m_HiddenWindowForRendering;
        private HwndSource GetHWND_FromHiddenWindowForRendering()
        {
            Window hiddenWindow = GetHideWindowForRendering();
            return PresentationSource.FromVisual(m_HiddenWindowForRendering) as HwndSource;
        }

        private Window GetHideWindowForRendering()
        {
            if (m_HiddenWindowForRendering != null)
                return m_HiddenWindowForRendering;
            m_HiddenWindowForRendering = new Window
            {
                Visibility = System.Windows.Visibility.Hidden,
                WindowStyle = System.Windows.WindowStyle.None,
                ShowInTaskbar = false,
                Background = System.Windows.Media.Brushes.Transparent,
                AllowsTransparency = true
            };
            m_HiddenWindowForRendering.Show();
            //m_HiddenWindowForRendering.Owner = System.Windows.Application.Current.MainWindow;
            m_HiddenWindowForRendering.Hide();
            return m_HiddenWindowForRendering;
        }

        /// <summary>
        /// If control size changed, need update size on OdGsDevice
        /// Note: Before update OdGsDevice need release D3DSurface
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SizeChangedHandler(object sender, SizeChangedEventArgs e)
        {
            if (m_RenderDevice.IsInitialized)
            {
                m_RenderDevice.UpdateSizeGsDevice();
            }
        }

        private static BitmapSource GetOdaLogo()
        {
            BitmapSource result;
            try
            {
                Bitmap source = Resource.TestImage;

                result = Imaging.CreateBitmapSourceFromHBitmap(
                  source.GetHbitmap(),
                  IntPtr.Zero,
                  Int32Rect.Empty,
                  BitmapSizeOptions.FromWidthAndHeight(source.Width, source.Height));
            }
            catch (Exception err)
            {
                LoggerHelper.Current.Error(err.Message, err);
                LoggerHelper.Current.Error(err.StackTrace, err);
                result = null;
            }

            return result;
        }

    }
}
